package com.zoe.onelink.redis.util;

import lombok.NonNull;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * <p>标题: </p>
 * <p>描述:
 *
 *         String lockKey = "myLockKey";
 *         String lockId = UUID.randomUUID().toString();
 *         try {
 *             // 创建一个60秒后自动过期的锁. 返回true: 获取锁成功，返回false：获取锁失败（开发者自行决定获取锁失败时处理方案）
 *             boolean status = RedisLockUtil.tryLock(lockKey, lockId, 60, TimeUnit.SECONDS);
 *             // 获取锁失败
 *             if (!status) {
 *                 // 执行获取锁失败的业务处理逻辑
 *                 throw ApiException.createEx(ExceptionCodeEnum.REDIS_TRY_GET_LOCK_FAILURE, lockKey);
 *             }
 *             // 执行获取锁成功后的业务逻辑
 *             // ...
 *         } finally {
 *             boolean unlockStatus = RedisLockUtil.unlock(lockKey, lockId);
 *             if (!unlockStatus) {
 *                 log.error("[Redis分布式锁] 解锁失败,lockKey={},lockId={}", lockKey, lockId);
 *             }
 *         }
 *
 * </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-10-28
 */
public class RedisLockUtil {

    public static final String LOCK_KEY_PREFIX = "lock:";
    private static final String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private static final RedisScript<Long> UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLOCK_LUA_SCRIPT, Long.class);

    /**
     * 尝试Redis锁
     *
     * @param lockKey  锁名称
     * @param lockId   锁持有者唯一ID
     * @param duration 过期时间(过期自动释放锁)
     * @param timeUnit 时间单位
     * @return true: 获取锁成功 , false: 获取锁失败
     */
    public static boolean tryLock(@NonNull String lockKey, @NonNull String lockId, long duration, @NonNull TimeUnit timeUnit) {
        return RedisUtil.setIfAbsent(LOCK_KEY_PREFIX.concat(lockKey), lockId, duration, timeUnit);
    }

    /**
     * 释放Redis锁
     *
     * @param lockKey 锁名称
     * @param lockId  锁持有者唯一ID
     */
    public static boolean unlock(@NonNull String lockKey, @NonNull String lockId) {
        Object result = RedisUtil.getRedisTemplate().execute(UNLOCK_REDIS_SCRIPT, Collections.singletonList(LOCK_KEY_PREFIX.concat(lockKey)), lockId);
        return Long.valueOf(1).equals(result);
    }

}
